<template>
  <div class="comp-side-graph-panel">
    <a-tabs
      size="small"
      default-active-key="systemComponent"
      @change="onTabChange"
    >
      <a-tab-pane key="myComponent" tab="我的组件" class="my-component">
        <div v-if="spinning" class="spin-box">
          <a-spin class="spin" :spinning="spinning"></a-spin>
        </div>
        <template v-if="!spinning && pipelineWidget.length === 0">
          <div class="empty-info">暂无组件</div>
        </template>
        <template v-else-if="pipelineWidget.length > 0 && !spinning">
          <div class="search-box">
            <a-input
              v-model="pipelineKeywords"
              placeholder="搜索内容"
              @change="searchPipelineWidget"
            />
          </div>
          <div class="favorite-diagrams-list">
            <a-collapse
              expand-icon-position="right"
              :active-key="
                pipelineWidget.map(
                  (list, index) => `${list.pipelineName}-${index}`
                )
              "
            >
              <a-collapse-panel
                v-for="(pipeline, index) in (pipelineKeywords.trim() ? pipelineSearchResult : pipelineWidget)"
                :key="`${pipeline.pipelineName}-${index}`"
                :header="pipeline.pipelineName"
              >
                <a-collapse
                  :active-key="
                    pipeline.taskWidgets.map(
                      (task, taskIdx) => `${task.taskName}-${taskIdx}`
                    )
                  "
                >
                  <a-collapse-panel
                    v-for="(taskList, taskIdx) in pipeline.taskWidgets || []"
                    :key="`${taskList.taskName}-${taskIdx}`"
                    :header="taskList.taskName"
                  >
                    <div
                      v-for="widget in taskList.widgets || []"
                      :key="widget.id"
                      class="chart-box"
                      draggable="true"
                      @mousedown.stop="onMouseDown2"
                      @dragstart="
                        (event) => {
                          onPipelineWidgetDragstart(event, widget)
                        }
                      "
                    >
                      <div class="chart-title">
                        {{ widget.name }}
                        <span class="delete-button">
                          <a-tooltip placement="top">
                            <template slot="title">
                              <span>删除</span>
                            </template>
                            <a-icon-font
                              class="icon"
                              type="iconicon-beifen5"
                              @click="onDelete(widget)"
                            />
                          </a-tooltip>
                        </span>
                      </div>
                      <div class="mask" style="pointer-events: none;"></div>
                      <div class="chart-box-real">
                        <Chart
                          :id="`grid-${widget.id}`"
                          v-bind="filterWidgetConfig(widget.id)"
                        >
                        </Chart>
                      </div>
                    </div>
                  </a-collapse-panel>
                </a-collapse>
              </a-collapse-panel>
            </a-collapse>
          </div>
        </template>
      </a-tab-pane>
      <a-tab-pane key="systemComponent" tab="系统组件">
        <div class="search-box">
          <a-input v-model="keywords" placeholder="搜索内容" />
        </div>
        <div class="tab-box">
          <a-tabs v-if="keywords === ''" tab-position="left" @change="callback">
            <a-tab-pane v-for="item in (Object.keys(chartTypes))" :key="item">
              <span slot="tab">
                <a-tooltip placement="left">
                  <template slot="title">
                    <span>{{ chartTypes[item].title }}</span>
                  </template>
                  <img
                    v-if="chartTypes[item].active"
                    :class="
                      chartTypes[item].iconClass
                        ? chartTypes[item].iconClass
                        : ''
                    "
                    :src="
                      publicPath + chartTypes[item].iconCharCode + '-active.svg'
                    "
                    alt=""
                    :style="{ width: '22px' }"
                  />
                  <img
                    v-else
                    :class="
                      chartTypes[item].iconClass
                        ? chartTypes[item].iconClass
                        : ''
                    "
                    :src="publicPath + chartTypes[item].iconCharCode + '.svg'"
                    alt=""
                    :style="{ width: '22px' }"
                  />
                </a-tooltip>
              </span>
              <div>
                <div
                  v-for="(chartItem, j) in systemComponents"
                  :key="`chart-${j}`"
                  class="chart-item"
                  draggable="true"
                  @mousedown.stop="onMouseDown"
                  @dragstart="
                    (event) => {
                      onDragstart(event, chartItem, item)
                    }
                  "
                >
                  <card size="small" style="pointer-events: none;">
                    <span
                      slot="title"
                      :style="{ 'font-weight': 600, color: '#5d637e' }"
                    >
                      {{ chartItem.title }}
                    </span>
                    <img
                      alt=""
                      :src="`${publicPath}${chartItem.imgSrc}`"
                      :style="{ height: '100%', width: '100%' }"
                    />
                  </card>
                </div>
              </div>
            </a-tab-pane>
          </a-tabs>
          <div v-else>
            <div>
              <div
                v-for="(chartItem, j) in systemComponents"
                :key="`chart-${j}`"
                class="chart-item-search"
                draggable="true"
                @mousedown.stop="onMouseDown"
                @dragstart="
                  (event) => {
                    onDragstart(event, chartItem, chartItem.chartType)
                  }
                "
              >
                <card size="small" style="pointer-events: none;">
                  <span
                    slot="title"
                    :style="{ 'font-weight': 600, color: '#5d637e' }"
                  >
                    {{ chartItem.title }}
                  </span>
                  <img
                    alt=""
                    :src="`${publicPath}${chartItem.imgSrc}`"
                    :style="{ height: '100%', width: '100%' }"
                  />
                </card>
              </div>
            </div>
          </div>
        </div>
      </a-tab-pane>
    </a-tabs>
  </div>
</template>
<script lang="ts">
import { Card } from 'ant-design-vue'
import { Vue } from 'vue-property-decorator'
import {
  chartMaps,
  chartTypes,
  GEOMAP_NAME,
  // getDefaultGraphConfig,
} from '@/config/contant'
import { widgetQueryByProject, getWidgetData, deleteWidget } from '@/api/widget'
import Chart from '@/components/chart/chart.vue'

import VisualizationStore from '@/store/modules/visualization'
import { cloneDeep, isString } from 'lodash'
import { dealData } from '@/components/studio/data/util'
import { removeNullEntries } from '@/util/recommend-visualization'
import { isArray } from '@/util/data-view-table-helper'
import { Widget } from '@/util/chart-widget'
import { publicPath } from '@/api/constants'

export default Vue.extend({
  components: {
    Card,
    Chart,
  },
  data() {
    return {
      mouseDownPos: {
        x: 0,
        y: 0,
      },
      startIndex: 0,
      keywords: '',
      selectedType: 'barChart',
      chartMaps,
      chartTypes,
      pipelineWidget: [], // 按pipeline维度组织的数据
      pipelineWidgetConfig: [], // pipeline下图表平铺数据
      pipelineKeywords: '',
      pipelineSearchResult: [],
      firstRender: false,
      spinning: false, // 控制我的组件loading状态
      // @ts-ignore
      publicPath,
    }
  },
  computed: {
    systemComponents() {
      const result: any[] = []
      Object.values(chartMaps).forEach((key) => {
        key.forEach((k) => {
          if (
            (this.keywords === '' && k.chartType === this.selectedType) ||
            (this.keywords !== '' &&
              (k.title.includes(this.keywords) ||
                k.name.toLowerCase().includes(this.keywords.toLowerCase())))
          ) {
            result.push(k)
          }
        })
      })
      return result
    },
  },
  mounted() {
    Object.keys(this.chartTypes).forEach((chartType: string) => {
      this.chartTypes[chartType].active = false
    })
    this.chartTypes.barChart.active = true
  },
  methods: {
    onMouseDown(event: any) {
      this.mouseDownPos.x = event.layerX - 6
      this.mouseDownPos.y = event.layerY - event.target.offsetTop
      // 这里用来把offset 先存储起来，方便drop时定位
      VisualizationStore.setDraggingOffset(this.mouseDownPos)
    },
    // 我的组件需要特殊处理
    onMouseDown2(event: any) {
      this.mouseDownPos.x = event.layerX - 6
      this.mouseDownPos.y = event.layerY
      // 这里用来把offset 先存储起来，方便drop时定位
      VisualizationStore.setDraggingOffset(this.mouseDownPos)
    },
    /**
     * 拖拽系统组件
     */
    onDragstart(event: any, data: any, chartType: string) {
      event.dataTransfer.setData('text', `${chartType}|${data.name}`)
    },
    /**
     * 拖拽pipeline里收藏的图表
     */
    onPipelineWidgetDragstart(event: any, widget: any) {
      // graph解析参数设置
      const { chartType = [] } = widget.data
      event.dataTransfer.setData('text', `${widget.id}$${chartType.join('|')}`)
    },
    callback(item: any) {
      this.selectedType = item
      Object.keys(chartTypes).forEach((key) => {
        chartTypes[key].active = key === item
      })
    },

    /** 检测数据是否为graph数据 */
    checkIsGraphData(itemData: any): boolean {
      return (
        itemData &&
        itemData.data &&
        isArray(itemData.data.nodes) &&
        isArray(itemData.data.links)
      )
    },

    /** 判断是否为地图组件 */
    checkIsGeoMapData(item: any) {
      return (
        item &&
        item.data &&
        isArray(item.data.chartType) &&
        (item.data.chartType as Array<string>).includes(GEOMAP_NAME)
      )
    },
    async onDelete(widget: Widget) {
      const responseData = await this.deleteWidget(widget)
      if (responseData.code === 100) {
        this.firstRender = false
        this.onTabChange('myComponent')
        this.$message.success('删除可视化成功')
      } else {
        this.$message.error('删除可视化失败')
      }
    },

    async deleteWidget(widget: Widget) {
      const response = await deleteWidget({
        data: {
          id: widget.id,
          projectId: localStorage.getItem('projectId'),
          type: 'task',
        },
      })
      return response.data // {result: null}
    },
    /**
     * 按照pipeline维度组织数据
     */
    parsePipelineWidget(pipelineList: any) {
      // const data:any = {}
      let widgetList: any[] = []
      pipelineList.forEach((pipeline: any) => {
        const { taskWidgets } = pipeline
        const taskList: any[] = []
        taskWidgets.forEach((task: any) => {
          if (task && task.widgets.length > 0) {
            taskList.push(task)
            widgetList = [
              ...widgetList,
              ...task.widgets.map((item: any) => ({
                ...item,
                pipelineName: pipeline.pipelineName,
                taskName: task.taskName,
              })),
            ]
          }
        })
        pipeline.taskWidgets = taskList
      })
      this.pipelineWidget = pipelineList.filter(
        (item: any) => item.taskWidgets.length > 0
      )
      // @ts-ignore
      this.pipelineWidgetConfig = widgetList.map((item: any) => {
        // 如果是graph组件，则需要对参数进行处理
        if (this.checkIsGeoMapData(item)) {
          // 修改数据结构错误
          if (isString(item.data.chartOptions.geoLayer[0])) {
            item.data.chartOptions.glyph = item.data.chartOptions
              .geoLayer[0] as string
            item.data.formData.glyph = item.data.chartOptions.glyph
          }
          item.data.formData.coordinateValue = item.data.formData.labelKey
          item.data.formData.encodingValue = isArray(
            item.data.formData.valueKey
          )
            ? item.data.formData.valueKey[0]
            : item.data.formData.valueKey
        }
        return item
      })
      VisualizationStore.updatePipelineWidgetList(widgetList)
    },

    /**
     * 根据图表配置查询图表的渲染数据
     */
    getWidgetData() {
      this.pipelineWidgetConfig.forEach((config: any, index: number) => {
        // 排除graph组件
        const query = {
          data: {
            id: config.data.formData.dataId,
            type: config.data.formData.dataType,
            widgetJson: config.data.widgetJson,
          },
        }
        getWidgetData(query)
          .then((response: any) => {
            if (response.data.code === 100) {
              const { result } = response.data
              const chartType = config.data.chartType[1]
              // 地图组件数据需要额外处理
              const widgetData = [
                'geographicMap',
                'pieChart',
                'donutChart',
              ].includes(chartType)
                ? dealData(result, chartType, config.data)
                : this.parseWidgetApiResult(response.data.result)
              config.data.chartOptions.size = [210, 200]
              // TODO: temp: remove null entries to avoid rendering error
              config.data.chartOptions.value = removeNullEntries(widgetData)
              if (index === this.pipelineWidgetConfig.length - 1) {
                this.spinning = false
              }
            }
          })
          .catch(() => {
            this.spinning = false
          })
      })
    },

    /**
     * 将接口返回数据转成图表接收的格式
     */
    parseWidgetApiResult({ columns = [], data = [] }: any) {
      const parsedData = data.map((item: []) => {
        const result = {}
        item.forEach((value, index) => {
          // @ts-ignore
          result[columns[index].name] = value
        })
        return result
      })

      return parsedData
    },

    clipGraphData(nodesData: Array<any>) {
      const padding = 10
      const minSize = [210, 165]
      let maxX = 0
      let maxY = 0
      let minX = 999999999
      let minY = 999999999
      nodesData.forEach((nodeData: any) => {
        maxX = Math.max(maxX, nodeData.x as number)
        minX = Math.min(minX, nodeData.x as number)
        maxY = Math.max(maxY, nodeData.y as number)
        minY = Math.min(minY, nodeData.y as number)
      })
      // 加上padding
      maxX += padding
      maxY += padding
      minX -= padding
      minY -= padding
      const size = [maxX - minX, maxY - minY]
      if (size[0] < minSize[0]) {
        const offsetX = (minSize[0] - size[0]) / 2
        minX -= offsetX
      }
      if (size[1] < minSize[1]) {
        const offsetY = (minSize[1] - size[1]) / 2
        minY -= offsetY
      }
      return {
        size,
        nodes: nodesData.map((item) => {
          return {
            ...item,
            x: item.x - minX,
            y: item.y - minY,
          }
        }),
      }
    },

    /* 获取缩放坐标点 */
    getNewPo(po: [number, number], center: [number, number], scale: number) {
      return [
        (po[0] - center[0]) * scale + center[0],
        (po[1] - center[1]) * scale + center[1],
      ]
    },

    /** 清除x、y,让它自动布局（侧边栏组件大小不足以放置大尺寸graph） */
    parseGraphConfig(config: any) {
      const data = config?.config?.graph?.value?.nodes
        ? config?.config?.graph?.value?.nodes
        : []
      if (data.length > 0) {
        let dataParse = this.clipGraphData(data)
        // 预览尺寸为 210 * 165
        const scale = Math.min(210 / dataParse.size[0], 165 / dataParse.size[1])
        if (scale < 1) {
          dataParse.nodes = dataParse.nodes.map((node: any) => {
            const pointNew = this.getNewPo(
              [node.x, node.y],
              dataParse.size as [number, number],
              scale
            )
            return {
              ...node,
              x: pointNew[0],
              y: pointNew[1],
            }
          })
          dataParse = this.clipGraphData(dataParse.nodes)
        }
        this.$set(config.config.graph.value, 'nodes', dataParse.nodes)
      }
      return config
    },

    /**
     * 根据图表id筛选配置信息
     */
    filterWidgetConfig(widgetId: string) {
      const target: any = cloneDeep(
        this.pipelineWidgetConfig.filter((item: any) => item.id === widgetId)[0]
      )
      if (target) {
        return {
          chartType: target.data.chartType,
          config: {
            ...target.data.chartOptions,
            titleIsShow: false,
            tooltipIsShow: false,
            legendIsShow: false,
            labelIsShow: false,
            xAxisTitleFontSize: 10,
            yAxisTitleFontSize: 10,
            axisTitleIsShow: false,
            xAxisRenderMode: 'simple',
            yAxisRenderMode: 'simple',
            padding: [10, 5, 5, 5],
            xAxisFontSize: 12,
            xAxisFontColor: '#373B52',
            yAxisFontColor: '#373B52',
            xAxisLineColor: '#e9e9e9',
            xAxisLineWidth: 1,
            yAxisLineColor: '#e9e9e9',
            xAxisGridColor: '#e9e9e9',
            yAxisGridColor: '#e9e9e9',
            xAxisTickColor: '#e9e9e9',
            yAxisTickColor: '#e9e9e9',
            size: [208, 168],
          },
        }
      }
      return {}
    },

    /**
     * 搜索pipeline及其下面的图表
     */
    searchPipelineWidget() {
      const keywords = this.pipelineKeywords.trim()
      if (!keywords) {
        return
      }
      const result: any[] = []
      this.pipelineWidget.forEach((pipeline: any) => {
        if (pipeline.pipelineName.includes(keywords)) {
          // pipeline名字包含关键字，则pipeline下全部图表都加入
          result.push(pipeline)
        } else {
          const tasks = pipeline.taskWidgets.filter((task: any) =>
            task.taskName.includes(keywords)
          )
          if (tasks.length > 0) {
            result.push({
              ...pipeline,
              taskWidgets: tasks,
            })
          } else {
            const data: any[] = []
            pipeline.taskWidgets.forEach((task: any) => {
              const widgets = task.widgets.filter((widget: any) =>
                widget.name.includes(keywords)
              ) // 图表名字包含关键字
              if (widgets.length > 0) {
                data.push({
                  taskName: task.taskName,
                  widgets,
                })
              }
            })
            if (data.length > 0) {
              result.push({
                ...pipeline,
                taskWidgets: data,
              })
            }
          }
        }
      })
      // @ts-ignore
      this.pipelineSearchResult = result
    },

    onTabChange(activeKey: string) {
      if (!this.firstRender && activeKey === 'myComponent') {
        this.firstRender = true
        this.spinning = true
        widgetQueryByProject({
          data: { projectId: this.$route.params.id },
        })
          .then((response) => {
            this.parsePipelineWidget(response.data.result)
            this.getWidgetData()
            setTimeout(() => {
              this.spinning = false
            }, 100)
          })
          .catch(() => {
            this.spinning = false
            this.firstRender = false
          })
      }
    },
  },
})
</script>
<style lang="less" scoped>
.comp-side-graph-panel {
  background: #fff;
  border-right: 1px solid #e9e9e9;
  height: 100%;
  width: 230px;

  /deep/ .ant-tabs {
    height: 100%;

    .ant-tabs-content {
      height: 100%;
    }

    .spin-box {
      height: auto;
      width: 220px;
    }

    .spin {
      display: block;
      margin: 48px auto;
    }
  }

  /deep/ .ant-tabs-top-bar {
    margin: 0;
  }

  .search-box {
    box-sizing: border-box;
    padding: 10px;
    width: 100%;

    /deep/ .ant-input {
      border-radius: 16px;
    }
  }

  .tab-box {
    background: #f9f9fc;
    height: calc(100% - 95px);
    overflow-x: hidden;
    overflow-y: scroll;
    transition: all ease 0.2s;
    width: 100%;

    &::-webkit-scrollbar {
      height: 6px;
      width: 6px;
    }

    &::-webkit-scrollbar-thumb {
      background: rgba(144, 147, 153, 0.5);
      border-radius: 6px;
    }

    &::-webkit-scrollbar-track {
      background: transparent;
      border-radius: 5px;
    }

    /deep/ .ant-tabs {
      height: 100%;

      .ant-tabs-tab span {
        font-size: 24px;
        margin-left: -11px;
      }
    }

    /deep/ .ant-tabs-nav-wrap {
      background: #fff;
      height: 100%;
    }

    /deep/ .ant-tabs-tab {
      border: 1px solid transparent;
      margin: 0;
      width: 40px;

      &.ant-tabs-tab-active {
        background: #f0f3ff !important;
      }

      &:hover {
        background-color: #f9f9fc;
      }
    }

    /deep/ .ant-tabs .ant-tabs-left-bar {
      .ant-tabs-ink-bar {
        display: none !important;
      }
    }

    /deep/ .ant-tabs .ant-tabs-left-content {
      padding-left: 0;
    }

    /deep/ .ant-card {
      border-width: 0;
      text-align: left;

      .ant-card-body {
        padding: 0;
      }

      .ant-card-head {
        background-color: #f6f8ff;
        border-bottom-width: 0;
      }
    }
  }

  .chart-item {
    background: #fff;
    border: 1px solid #e9e9eb;
    border-radius: 4px;
    cursor: pointer;
    height: 122px;
    line-height: 108px;
    margin-left: 5px;
    text-align: center;
    width: 170px;

    &:hover {
      border: 1px solid #dfe1fd;
    }
  }

  .chart-item-search {
    background: #fff;
    border: 1px solid #e9e9eb;
    border-radius: 4px;
    cursor: pointer;
    height: 142px;
    line-height: 108px;
    margin-bottom: 5px;
    margin-left: 5px;
    text-align: center;
    width: calc(100% - 10px);

    &:hover {
      border: 1px solid #dfe1fd;
    }
  }

  .chart-item + .chart-item {
    margin-top: 10px;
  }

  .my-component {
    overflow-y: auto;
    padding-bottom: 30px;

    &::-webkit-scrollbar {
      height: 6px;
      width: 6px;
    }

    &::-webkit-scrollbar-thumb {
      background: rgba(144, 147, 153, 0.5);
      border-radius: 6px;
    }

    &::-webkit-scrollbar-track {
      background: transparent;
      border-radius: 5px;
    }

    /deep/ .ant-collapse-content-box {
      padding: 0;
    }

    /deep/ .ant-collapse,
    .ant-collapse > .ant-collapse-item,
    /deep/ .ant-collapse-content {
      border: 0;
    }

    /deep/ .ant-collapse {
      margin-top: 10px;

      &.ant-collapse-icon-position-left .ant-collapse-header {
        padding-left: 40px;
      }
    }

    /deep/ .ant-collapse {
      background-color: #fff;
    }

    /deep/ .ant-collapse-header {
      padding: 5px 16px;
    }

    .empty-info {
      height: 40px;
      line-height: 40px;
      text-align: center;
    }

    .chart-box {
      border: 1px solid #e9e9eb;
      border-radius: 4px;
      cursor: pointer;
      height: 200px;
      margin: 10px auto 0;
      overflow: hidden;
      position: relative;
      width: 210px;

      // * {
      //   pointer-events: none;
      // }

      .chart-title {
        background-color: #f9faff;
        color: rgb(93, 99, 126);
        font-size: 14px;
        font-weight: 600;
        height: 30px;
        line-height: 30px;
        padding: 0 12px;

        .delete-button {
          float: right;
        }
      }

      .chart-box-real {
        height: 168px !important;
        pointer-events: none;
        position: relative;
        width: 208px !important;
      }

      .mask {
        bottom: 0;
        left: 0;
        position: absolute;
        right: 0;
        top: 0;
        z-index: 310;
      }
    }
  }

  .icon-map {
    transform: scale(1.2);
  }
}

// .favorite-diagrams-list {
//   max-height: 650px;
//   overflow-y: scroll;
// }
</style>
